#ifndef _STAIRMATH_H_
#define _STAIRMATH_H_

#define COS(c) { __asm fld c \
__asm fcos \
__asm fstp c }

#define SIN(s) { __asm fld s \
__asm fsin \
__asm fstp s }

#define SQRT(s) { __asm fld s \
__asm fsqrt \
__asm fstp s }


class CMatrix3x3 // Uses OpenGL standard for element storage
{
public:

    CMatrix3x3(void);
    CMatrix3x3(float i11, float i12, float i13,
					   float i21, float i22, float i23,
					   float i31, float i32, float i33);
    inline float &operator()( unsigned long dwRow, unsigned long dwCol )
	{ 
		return m_f[dwCol*3+dwRow];
	};
	inline float const &operator()( unsigned long dwRow, unsigned long dwCol ) const
	{
		return m_f[dwCol*3+dwRow];
	}
	inline CMatrix3x3&operator+=(CMatrix3x3 const& m)
	{
		float* pf = (float*)m.m_f;
		for (register int i=0; i < 3*3; i++)
			m_f[i] += pf[i];
		return *this;
	}
	inline CMatrix3x3 operator*(CMatrix3x3 const &m)
	{
		CMatrix3x3 mRes;

		for(int i = 0;i < 3;i++)
		{
			for(int j = 0;j < 3;j++)
			{
				float Value = 0;
          
				for(int k = 0;k < 3;k++)
				{
					Value += GetElement(i,k) * m(k,j);
				}

				mRes.SetElement(i,j,Value);
			}
		}

		return mRes;
	}
	inline CMatrix3x3 operator*(float const &f)
	{
		CMatrix3x3 mRes;
		float* pV = mRes.GetRawElements();
		for (register int i=0; i < 3*3; i++)
			pV[i] = m_f[i] * f;
		return mRes;
	}
	inline _VECTOR operator*(_VECTOR const& v)
	{
		_VECTOR vRes;
		vRes.x = v.x * m_f[0] + v.y * m_f[3] + v.z * m_f[6];
		vRes.y = v.x * m_f[1] + v.y * m_f[4] + v.z * m_f[7];
		vRes.z = v.x * m_f[2] + v.y * m_f[5] + v.z * m_f[8];
		return vRes;
	};

	void ZeroMatrix();
	void Identity();
	void Invert();
	void Orthonormalize();
	inline CMatrix3x3 GetTranspose()
	{
		CMatrix3x3 mRes;

		for (register int i = 0; i < 3; i++)
		{
			mRes(0,i) = GetElement(i,0);
			mRes(1,i) = GetElement(i,1);
			mRes(2,i) = GetElement(i,2);
		}

		return mRes;
	}
	inline float* GetRawElements() { return m_f; };

protected:

	inline float GetElement(unsigned long dwRow, unsigned long dwCol) const
	{
		return m_f[dwCol*3+dwRow];
	}
	inline void SetElement(unsigned long dwRow, unsigned long dwCol, float fVal)
	{
		m_f[dwCol*3+dwRow] = fVal;
	}
	float m_f[3*3];
};


inline float Magnitude(_VECTOR& v)
{
	float d = v.x * v.x + v.y * v.y + v.z * v.z;
	SQRT(d);
	return d;
}

inline float DotProduct(const _VECTOR& v1, const _VECTOR& v2)
{
	return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}

inline void Normalize(_NORMAL& n)
{
	float m = n.a * n.a + n.b * n.b + n.c * n.c;
	SQRT(m);
	n.a /= m; n.b /= m; n.c /= m;
}

inline void Normalize(_VECTOR& v)
{
	float d = Magnitude(v);
	v.x /= d; v.y /= d; v.z /= d;
}

inline void ZeroVertex(_VERTEX& v)
{
	v.x = v.y = v.z = 0;
}

inline void ZeroVector(_VECTOR& v)
{
	v.x = v.y = v.z = 0;
}

inline _VERTEX&operator +=(_VERTEX& v1, _VERTEX& v2)
{
	v1.x += v2.x;
	v1.y += v2.y;
	v1.z += v2.z;
	return v1;
}

inline _VERTEX&operator +=(_VERTEX& v1, _VECTOR& v2)
{
	v1.x += v2.x;
	v1.y += v2.y;
	v1.z += v2.z;
	return v1;
}

inline _VERTEX operator *(_VERTEX& v, float fScalar)
{
	_VERTEX vc;
	vc.x = v.x * fScalar;
	vc.y = v.y * fScalar;
	vc.z = v.z * fScalar;
	return vc;
}

inline _VECTOR operator -(_VERTEX& v1, _VERTEX& v2)
{
	_VECTOR v;
	v.x = v1.x - v2.x;
	v.y = v1.y - v2.y;
	v.z = v1.z - v2.z;
	return v;
}

inline _VECTOR operator +(_VECTOR& v1, _VECTOR& v2)
{
	_VECTOR v;
	v.x = v1.x + v2.x;
	v.y = v1.y + v2.y;
	v.z = v1.z + v2.z;
	return v;
}

inline _VECTOR operator -(_VECTOR& v1, _VECTOR& v2)
{
	_VECTOR v;
	v.x = v1.x - v2.x;
	v.y = v1.y - v2.y;
	v.z = v1.z - v2.z;
	return v;
}

inline _VECTOR operator -(_VECTOR& v1)
{
	_VECTOR v;
	v.x = -v1.x;
	v.y = -v1.y;
	v.z = -v1.z;
	return v;
}

inline _VECTOR&operator +=(_VECTOR& v1, _VECTOR& v2)
{
	v1.x += v2.x;
	v1.y += v2.y;
	v1.z += v2.z;
	return v1;
}

inline _VECTOR operator *(_VECTOR& v1, _VECTOR& v2)
{
	_VECTOR vc;
	vc.x = v1.y*v2.z - v1.z*v2.y;
	vc.y = v1.z*v2.x - v1.x*v2.z;
	vc.z = v1.x*v2.y - v1.y*v2.x;
	return vc;
}

inline _VECTOR operator *(_VECTOR& v, float fScalar)
{
	_VECTOR vc;
	vc.x = v.x * fScalar;
	vc.y = v.y * fScalar;
	vc.z = v.z * fScalar;
	return vc;
}

inline _VECTOR operator /(_VECTOR& v, float fScalar)
{
	_VECTOR vc;
	vc.x = v.x / fScalar;
	vc.y = v.y / fScalar;
	vc.z = v.z / fScalar;
	return vc;
}

void CalculateNormal(const _VERTEX* a, const _VERTEX* b, const _VERTEX* c, 
					 _NORMAL& n);
void Rotate_Arbitrary(_VERTEX* v, const _VECTOR* vAxis, const _VERTEX* vCenter, const float radians);
void shadowMatrix(GLfloat shadowMat[4][4],
  _NORMAL& vnGround, _VECTOR& vLight);

#endif